﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		CategoryService.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Dexter.Caching;
using Dexter.Core.EventArgs;
using Dexter.DataTransferObject;
using Dexter.Domain.Model;
using Dexter.Logger.Contracts;
using Dexter.Repository;

namespace Dexter.Services.Implementation {
	/// <summary>
	/// 	The implementation of <see cref = "ICategoryService" />.
	/// </summary>
	public class CategoryService : ICategoryService {
		/// <summary>
		/// 	An instance of a concrete implementation of <see cref = "ICategoryRepository" />
		/// </summary>
		protected readonly ICategoryRepository CategoryRepository;

		/// <summary>
		/// 	An instance of a concrete implementation of <see cref = "ICacheProvider" />
		/// </summary>
		protected ICacheProvider CacheProvider;

		/// <summary>
		/// 	The logger.
		/// </summary>
		protected readonly ILogger Logger;

		/// <summary>
		/// Initializes a new instance of the <see cref="CategoryService"/> class.
		/// </summary>
		/// <param name="categoryRepository">The category repository.</param>
		/// <param name="logger">The logger.</param>
		/// <param name="cacheProvider">The cache provider.</param>
		public CategoryService ( ICategoryRepository categoryRepository, ILogger logger, ICacheProvider cacheProvider ) {
			CategoryRepository = categoryRepository;
			Logger = logger;
			CacheProvider = cacheProvider;
		}

		/// <summary>
		/// 	Gets the default category with child and parent.
		/// </summary>
		/// <returns>
		/// 	An instance of Category with all tree loaded
		/// </returns>
		public virtual Category GetDefaultCategoryWithChilds ( ) {
			Category data = CategoryRepository.GetDefaultCategoryWithChilds ( true );

			DefaultCategoryRetrieved.Raise ( this , new GenericEventArgs <Category> ( data ) );

			return data;
		}

		/// <summary>
		/// 	Gets the default category with child and parent.
		/// </summary>
		/// <returns>An instance of Category with all tree loaded</returns>
		public CategoryDto GetDefaultCategoryDtoWithChilds ( ) {
			var value = CacheProvider.Get<CategoryDto> ( "GetDefaultCategoryDtoWithChilds", CacheRegions.Categories )
				.Return ( o => o,
						  ( ) =>
						  {
							  var c = CategoryRepository.GetDefaultCategoryWithChilds ( false ).ToDto ( );

							  CacheProvider.Put ( "GetDefaultCategoryDtoWithChilds", c, TimeSpan.FromHours ( 5 ), CacheRegions.Categories, null );

							  return c;
						  } );

			return value;
		}

		/// <summary>
		/// 	Retrieve a Category by its name.
		/// </summary>
		/// <param name = "key">The key.</param>
		/// <returns>
		/// 	Null if there isn't a category wich specified name, otherwise an instance of Category.
		/// 	The Parent and Childs are not fetched.
		/// </returns>
		/// <exception cref = "ArgumentOutOfRangeException"> If <paramref name = "key" /> is lesser than 0.</exception>
		public virtual Category GetCategoryByKeyWithChilds ( int key  ) {
			Category data = CategoryRepository.GetCategoryByKeyWithChilds ( key , true );

			CategoryRetrieved.Raise ( this , new GenericEventArgs <Category> ( data ) );

			return data;
		}

		///<summary>
		///	Retrieve a Category by its name.
		///</summary>
		///<param name = "key">The key.</param>
		///<returns>
		///	Null if there isn't a category wich specified name, otherwise an instance of Category.
		///	The Parent and Childs are not fetched.
		///</returns>
		///<exception cref="ArgumentOutOfRangeException"> If <paramref name="key"/> is lesser than 0.</exception>
		public CategoryDto GetCategoryDtoByKeyWithChilds ( int key ) {
			string cacheKey = string.Format ( "GetCategoryDtoByKeyWithChilds- {0}" , key );
			var value = CacheProvider.Get<CategoryDto> ( cacheKey, CacheRegions.Categories )
				.Return ( o => o,
						  ( ) =>
						  {
							  var c = CategoryRepository.GetCategoryByKeyWithChilds ( key, false ).ToDto ( );

							  CacheProvider.Put ( cacheKey, c, TimeSpan.FromHours ( 5 ), CacheRegions.Categories, null );

							  return c;
						  } );

			return value;
		}

		///<summary>
		///	Retrieve a Category by its name.
		///</summary>
		///<param name = "name">The name of the category.</param>
		///<returns>
		///	Null if there isn't a category wich specified name, otherwise an instance of Category.
		///	The Parent and Childs are not fetched.
		///</returns>
		public virtual Category GetCategoryByNameWithChilds ( string name ) {
			Category data = CategoryRepository.GetCategoryByNameWithChilds(name, true);

			CategoryRetrieved.Raise(this, new GenericEventArgs<Category>(data));

			return data;
		}

		///<summary>
		///	Retrieve a Category by its name.
		///</summary>
		///<param name = "name">The name of the category.</param>
		///<returns>
		///	Null if there isn't a category wich specified name, otherwise an instance of Category.
		///	The Parent and Childs are not fetched.
		///</returns>
		///<exception cref="ArgumentOutOfRangeException"> If <paramref name="name"/> is <c>null</c> o empty.</exception>
		public CategoryDto GetCategoryDtoByNameWithChilds ( string name ) {
			if(string.IsNullOrEmpty ( name ))
				throw new ArgumentException ( "Invalid Categori Name" );


			string cacheKey = string.Format ( "GetCategoryDtoByNameWithChilds- {0}", name );
			var value = CacheProvider.Get<CategoryDto> ( cacheKey, CacheRegions.Categories )
				.Return ( o => o,
						  ( ) =>
						  {
							  var c =  CategoryRepository.GetCategoryByNameWithChilds(name, false).ToDto ( );

							  CacheProvider.Put ( cacheKey, c, TimeSpan.FromHours ( 5 ), CacheRegions.Categories, null );

							  return c;
						  } );

			return value;
		}

		/// <summary>
		/// 	Deletes the specified category and moved the into into another.
		/// </summary>
		/// <param name = "category">The category to delete.</param>
		/// <param name = "newCategory">The new category for the posts of the old category.</param>
		public virtual void DeleteCategory ( Category category , Category newCategory ) {
			var e = new CancelEventArgs ( );
			CategoryDeleting.Raise ( this , e );

			if ( e.Cancel ) {
				return;
			}

			CategoryRepository.DeleteCategory ( category , newCategory );

			CacheProvider.ClearRegion ( CacheRegions.Categories );

			CategoryDeleted.Raise ( e , new System.EventArgs ( ) );
		}

		/// <summary>
		/// 	Saves the specified category.
		/// </summary>
		/// <param name = "category">The category to save.</param>
		public virtual void Save ( Category category ) {
			var e = new CancelEventArgs ( );
			CategorySaving.Raise ( this , e );

			if ( e.Cancel ) {
				return;
			}

			CategoryRepository.Save ( category );

			CacheProvider.ClearRegion ( CacheRegions.Categories );

			CategorySaved.Raise ( this , new GenericEventArgs <Category> ( category ) );
		}

		/// <summary>
		/// 	Updates the specified category.
		/// </summary>
		/// <param name = "category">The category to update.</param>
		public virtual void Update ( Category category ) {
			var e = new CancelEventArgs ( );
			CategoryUpdating.Raise ( this , e );

			if ( e.Cancel ) {
				return;
			}

			CategoryRepository.Update ( category );

			CacheProvider.ClearRegion ( CacheRegions.Categories );

			CategoryUpdated.Raise ( this , new GenericEventArgs <Category> ( category ) );
		}

		/// <summary>
		/// Updates the category statistics.
		/// </summary>
		public void UpdateCategoryStatistics ( ) {
			CategoryRepository.UpdateCategoryStatistics ( );
		}

		/// <summary>
		/// 	Retrurn a flat views of all categories from the repository.
		/// </summary>
		/// <returns>
		/// 	The return value will never be null, there is always the default category.
		/// 	The tree il completely loaded.
		/// </returns>
		public virtual IEnumerable <Category> GetCategoriesStructure ( ) {
			IEnumerable <Category> data = CategoryRepository.GetCategoriesStructure ( );

			CategoriesRetrieved.Raise ( this , new GenericEventArgs <IEnumerable <Category>> ( data ) );

			return data;
		}

		///<summary>
		///	Retrurn a flat views of all root categories from the repository.
		///</summary>
		///<returns>
		///	The return value will never be null, there is always the default category.
		///	The tree il completely loaded.
		///</returns>
		public IList<CategoryDto> GetCategoriesDtoStructure()
		{
			var value = CacheProvider.Get<IList<CategoryDto>>("GetCategoriesDtoStructure", CacheRegions.Categories)
				.Return ( o => o,
						  ( ) =>
						  {
							  IList<CategoryDto> c = CategoryRepository.GetCategoriesStructure(false).ToDtos();

							  CacheProvider.Put ( "GetCategoriesDtoStructure", c, TimeSpan.FromHours ( 5 ), CacheRegions.Categories, null );

							  return c;
						  } );

			return value;
		}

		///<summary>
		///	Retrurn a flat views of all categories from the repository.
		///</summary>
		/// <remarks>
		///		The returned objects will not have the tracking system enabled!
		/// </remarks>
		///<returns>
		///	The return value will never be null, there is always the default category.
		///	The tree il completely loaded.
		///</returns>
		public IEnumerable <Category> GetAllCategories ( ) {
			return CategoryRepository.GetAllCategories ( false );
		}

		/// <summary>
		/// 	This event will raise when the default <see cref = "Category" /> will be retrieved by the implementation of <see cref = "ICategoryService.GetDefaultCategoryWithChilds()" />.
		/// </summary>
		public virtual event EventHandler <GenericEventArgs <Category>> DefaultCategoryRetrieved;

		/// <summary>
		/// 	This event will raise before a <see cref = "Category" /> will retrieved by the implementation of <see cref = "GetCategoryByKeyWithChilds(int)" />.
		/// </summary>
		public virtual event EventHandler <GenericEventArgs <Category>> CategoryRetrieved;

		/// <summary>
		/// 	This event will raise before a <see cref = "Category" /> will retrieved by the implementation of <see cref = "GetCategoryByNameWithChilds(string)" />.
		/// </summary>
		public event EventHandler <GenericEventArgs <Category>> CategoryRetrievedByName;

		/// <summary>
		/// 	This event will raise when a <see cref = "Category" /> will be retrieved by the implementation of <see cref = "GetCategoryByKeyWithChilds(int)" />.
		/// </summary>
		public virtual event EventHandler <GenericEventArgs <IEnumerable <Category>>> CategoriesRetrieved;

		/// <summary>
		/// 	This event will raise when a <see cref = "Category" /> will be deleted by the implementation of <see cref = "DeleteCategory(Category, Category)" />.
		/// </summary>
		public virtual event EventHandler <System.EventArgs> CategoryDeleted;

		/// <summary>
		/// 	This event will raise before a <see cref = "Category" /> will deleted by the implementation of <see cref = "DeleteCategory(Category, Category)" />.
		/// </summary>
		public virtual event EventHandler <CancelEventArgs> CategoryDeleting;

		/// <summary>
		/// 	This event will raise when a <see cref = "Category" /> will be deleted by the implementation of <see cref = "Save(Category)" />.
		/// </summary>
		public virtual event EventHandler <GenericEventArgs <Category>> CategorySaved;

		/// <summary>
		/// 	This event will raise before a <see cref = "Category" /> will deleted by the implementation of <see cref = "Save(Category)" />.
		/// </summary>
		public virtual event EventHandler <CancelEventArgs> CategorySaving;

		/// <summary>
		/// 	This event will raise when a <see cref = "Category" /> will be updated by the implementation of <see cref = "Update(Category)" />.
		/// </summary>
		public virtual event EventHandler<GenericEventArgs<Category>> CategoryUpdated;

		/// <summary>
		/// 	This event will raise before a <see cref = "Category" /> will updated by the implementation of <see cref = "Update(Category)" />.
		/// </summary>
		public virtual event EventHandler<CancelEventArgs> CategoryUpdating;
	}
}
